package com.buwei.infrastructure.aop;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.buwei.infrastructure.annotation.UserOperationLog;
import com.buwei.infrastructure.config.EventPublisher;
import com.buwei.infrastructure.utils.NetworkUtil;
import com.buwei.module.log.domain.event.UserOperatedEvent;
import com.google.gson.Gson;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
@Component
public class UserOperationLogAspect {

	@Autowired
	EventPublisher eventPublisher;
	
	private Gson gson = new Gson();

	ThreadLocal<Long> startTime = new ThreadLocal<Long>();
    ThreadLocal<String> tag=new ThreadLocal<String>();

	// 申明一个切点 里面是 execution表达式
	@Pointcut("execution(public * com.buwei.module.rbac.ui.controller..*.*(..))")
	private void controllerAspect() {
	}
	
	// 请求method前打印内容
//	@Before(value = "controllerAspect()")
	@Before("@annotation(userOperationLog)")
	public void methodBefore(JoinPoint joinPoint, UserOperationLog userOperationLog) {
		startTime.set(System.currentTimeMillis());
		tag.set(IDGeneratorUtil.getUUID());

		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes();
		HttpServletRequest request = requestAttributes.getRequest();
		
		String currentUser = "nobody";
//		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//		if (authentication == null || !authentication.isAuthenticated()) {
//			currentUser = "nobody";
//		} else {
//			currentUser = authentication.getName();
//		}

		ZoneId bjZone = ZoneId.of("GMT+08:00");
		LocalDateTime now = LocalDateTime.now(bjZone);  
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");  
        String reqTime = now.format(format);

		// 打印请求内容
		log.info("-------------------- 请求 BEGIN --------------------");
		log.info("日志标识:" + tag.get());
		log.info("请求时间:" + reqTime);
		log.info("请求用户名:" + currentUser);
		log.info("请求地址:" + NetworkUtil.getIpAddress(request));
		log.info("请求URL:" + request.getRequestURL().toString());
		log.info("请求模块:" + userOperationLog.moduleName());
		log.info("请求描述:" + userOperationLog.operationDesc());
		log.info("请求方式:" + request.getMethod());
		log.info("请求类方法:" + joinPoint.getSignature());
		log.info("请求类方法参数:" + Arrays.toString(joinPoint.getArgs()));
		log.info("-------------------- 请求 END --------------------");
		
		UserOperatedEvent event = UserOperatedEvent.builder()
			.tag(tag.get())
			.username(currentUser)
			.remoteAddr(NetworkUtil.getIpAddress(request))
			.moduleName(userOperationLog.moduleName())
			.operationDesc(userOperationLog.operationDesc())
			.requestUri(request.getRequestURI())
			.method(request.getMethod())
			.createdAt(now)
			.build();
		eventPublisher.publishEvent(event);
		
	}

	// 在方法执行完结后打印返回内容
//	@AfterReturning(returning = "o", pointcut = "controllerAspect()")
	@AfterReturning(returning = "o", pointcut = "@annotation(userOperationLog)")
	public void methodAfterReturing(Object o, UserOperationLog userOperationLog) {
		ZoneId bjZone = ZoneId.of("GMT+08:00");
		LocalDateTime now = LocalDateTime.now(bjZone);  
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");  
        String resTime = now.format(format);
        
		log.info("-------------------- 返回 BEGIN --------------------");
		log.info("日志标识:" + tag.get());
		log.info("响应时间:" + resTime);
		log.info("响应内容:" + gson.toJson(o));
		long timeConsuming = System.currentTimeMillis() - startTime.get();
		log.info("耗时:" + timeConsuming + "ms");
		log.info("-------------------- 返回 END --------------------");
		
	}
	
}
